package com.itzdm.file.service.Impl;

import com.itzdm.common.utils.MinioUtils;
import com.itzdm.file.domain.dto.ChunkMergeDTO;
import com.itzdm.file.domain.dto.ChunkUploadDTO;
import com.itzdm.file.domain.vo.FileVO;
import com.itzdm.file.service.ChunkUploadService;
import com.itzdm.file.service.FileService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 分片上传服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ChunkUploadServiceImpl implements ChunkUploadService {

    @Value("${file.upload.chunk.dir:/tmp/chunk_upload}")
    private String chunkUploadDir;

    @Value("${file.upload.temp.dir:/tmp/upload_temp}")
    private String tempDir;

    private final FileService fileService;
    private final MinioUtils minioUtils;

    // 使用ConcurrentHashMap存储分片上传状态和信息
    private final Map<String, Map<Integer, String>> chunkInfoMap = new ConcurrentHashMap<>();

    @Override
    public boolean uploadChunk(ChunkUploadDTO chunkUploadDTO) {
        MultipartFile file = chunkUploadDTO.getFile();
        String fileHash = chunkUploadDTO.getFileHash();
        Integer chunkIndex = chunkUploadDTO.getChunkIndex();
        String fileName = chunkUploadDTO.getFileName();

        if (file == null || fileHash == null || chunkIndex == null || fileName == null) {
            return false;
        }

        try {
            // 将所有的文件分片都放在内存中，创建目录
            String chunkDir = chunkUploadDir + File.separator + fileHash;
            Path path = Paths.get(chunkDir);
            if (!Files.exists(path)) {
                Files.createDirectories(path);
            }

            // 将分片文件保存到本地
            String chunkFilePath = chunkDir + File.separator + chunkIndex;
            Path tempPath = Paths.get(tempDir);
            file.transferTo(tempPath);

            // 将上传成功的上传到内存
            chunkInfoMap.computeIfAbsent(fileHash, k -> new ConcurrentHashMap<>()).put(chunkIndex, chunkFilePath);
            log.info("分片上传成功: fileHash={}, chunkIndex={}", fileHash, chunkIndex);
            return true;
        } catch (Exception e) {
            log.error("分片上传失败", e);
            return false;
        }
    }

    @Override
    public boolean checkChunk(String fileHash, Integer chunkIndex) {
        if (fileHash == null || chunkIndex == null) {
            return false;
        }

        // 检查内存中是否存在该分片记录
        Map<Integer, String> chunks = chunkInfoMap.get(fileHash);
        if (chunks != null && chunks.containsKey(chunkIndex)) {
            return true;
        }

        // 如果内存中没有，检查本地文件系统
        String chunkFilePath = chunkUploadDir + File.separator + fileHash + File.separator + chunkIndex;
        return Files.exists(Paths.get(chunkFilePath));
    }

    @Override
    public FileVO mergeChunks(ChunkMergeDTO chunkMergeDTO) {
        String fileHash = chunkMergeDTO.getFileHash();
        String fileName = chunkMergeDTO.getFileName();
        Integer totalChunks = chunkMergeDTO.getTotalChunks();

        if (fileHash == null || fileName == null || totalChunks == null) {
            log.error("合并分片参数不完整");
            return null;
        }

        // 获取该文件的所有分片信息
        Map<Integer, String> chunks = chunkInfoMap.get(fileHash);
        if (chunks == null || chunks.size() != totalChunks) {
            log.error("分片不完整，无法合并: 预期={}, 实际={}", totalChunks, chunks != null ? chunks.size() : 0);
            return null;
        }

        try {
            // 创建临时目录
            Path tempDirPath = Paths.get(tempDir);
            if (!Files.exists(tempDirPath)) {
                Files.createDirectories(tempDirPath);
            }

            // 生成临时合并文件路径
            String tempFileName = UUID.randomUUID() + "_" + fileName;
            String tempFilePath = tempDir + File.separator + tempFileName;
            Path mergedPath = Paths.get(tempFilePath);

            // 创建合并文件
            if (!Files.exists(mergedPath)) {
                Files.createFile(mergedPath);
            }

            // 合并分片
            try (FileOutputStream fos = new FileOutputStream(tempFilePath)) {
                for (int i = 0; i < totalChunks; i++) {
                    String chunkFilePath = chunks.get(i);
                    if (chunkFilePath == null) {
                        log.error("分片文件路径不存在: fileHash={}, chunkIndex={}", fileHash, i);
                        return null;
                    }

                    Path chunkPath = Paths.get(chunkFilePath);
                    if (!Files.exists(chunkPath)) {
                        log.error("本地分片文件不存在: {}", chunkFilePath);
                        return null;
                    }

                    try (InputStream inputStream = Files.newInputStream(chunkPath)) {
                        IOUtils.copy(inputStream, fos);
                    }
                }
            }

            // 创建File对象
            File mergedFile = new File(tempFilePath);

            // 上传到MinIO并保存信息到数据库
            FileVO fileVO = fileService.uploadFile(mergedFile, fileName);

            // 清理临时文件
            mergedFile.delete();

            // 清理分片和内存中的记录
            cleanupChunks(fileHash, totalChunks, chunks);
            chunkInfoMap.remove(fileHash);

            log.info("分片合并成功并上传到MinIO: fileHash={}, fileName={}", fileHash, fileName);
            return fileVO;

        } catch (Exception e) {
            log.error("合并分片失败", e);
            return null;
        }
    }

    /**
     * 清理分片文件
     *
     * @param fileHash    文件哈希
     * @param totalChunks 总分片数
     * @param chunks      分片信息
     */
    private void cleanupChunks(String fileHash, int totalChunks, Map<Integer, String> chunks) {
        try {
            // 删除本地分片文件
            String chunkDir = chunkUploadDir + File.separator + fileHash;

            // 删除各个分片
            for (int i = 0; i < totalChunks; i++) {
                String chunkFilePath = chunks.get(i);
                if (chunkFilePath != null) {
                    Files.deleteIfExists(Paths.get(chunkFilePath));
                }
            }

            // 删除分片目录
            Files.deleteIfExists(Paths.get(chunkDir));
        } catch (Exception e) {
            log.error("清理分片文件失败", e);
        }
    }
} 